/*************************************************
 Copyright (c) 2019
 All rights reserved.
 File name:     dlt645_port.c
 Description:   DLT645 移植&使用例程文件
 History:
 1. Version:    
    Date:       2019-09-19
    Author:     wangjunjie
    Modify:     
*************************************************/
#include "dlt645.h"
//#include "rtthread.h"
#include <stdlib.h>
#include <semaphore.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include "dlt645_private.h"

//DLT645采集使用的串口名

//DL/T 645硬件拓展结构体
typedef struct{
	char* filename;
	int rate;
	int fd;
	struct termios *tty;
}UartDevice;
//645结构体注册

//这个是一个重要的结构体，保存着基本的配置信息
static UartDevice dev = {
	.filename = "/dev/ttyUSB0",
	.rate = B2400,        
};
// dlt645_t g_dlt645;



//dlt645 环境结构体
//extern dlt645_t dlt645;

/**
 * Name:    dlt645_hw_read
 * Brief:   dlt645 硬件层接收数据
 * Input:
 *  @ctx:   645运行环境
 *  @msg:   接收数据存放地址
 *  @len:   数据最大接收长度 
 * Output:  读取数据的长度
 */
static int dlt645_hw_read(dlt645_t *ctx, uint8_t *msg ,uint16_t len)
{
	int rc;
	rc = read(((UartDevice *)(ctx->port_data))->fd, msg, len - 1);
	if (rc < 0) {
		printf("%s: failed to read uart data\r\n", __func__);
		return rc;
	}
	msg[rc] = '\0';
	return rc;
}


/**
 * Name:    dlt645_hw_write
 * Brief:   dlt645 硬件层发送数据
 * Input:
 *  @ctx:   645运行环境
 *  @buf:   待发送数据
 *  @len:   发送长度
 * Output:  实际发送的字节数，错误返回-1
 */
static int dlt645_hw_write(dlt645_t *ctx, uint8_t *buf, uint16_t len)
{
	return write(((UartDevice *)(ctx->port_data))->fd, buf, len);
}


/**
 * Name:    dlt645_port_init
 * Brief:   645采集硬件层初始化
 * Input:   None
 * Output:  None
 * 
 * const char *__path
 */
int dlt645_port_init(const char *device){
	struct termios *tty;
	int fd;
	int rc;

	//fd = open(dev.filename, O_RDWR);
	//O_RDWR可读可写，O_NOCTTY单片机串口，不属于shell界面
	//fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
	fd = open(device, O_RDWR | O_NOCTTY);
	if (fd < 0) {
		printf("%s: failed to open UART device\r\n", __func__);
		return fd;
	}else{
		printf("success open\r\n");
	}

	tty = malloc(sizeof(*tty));
	if (!tty) {
		printf("%s: failed to allocate UART TTY instance\r\n", __func__);
		return -ENOMEM;
	}

	memset(tty, 0, sizeof(*tty));

	// /*
	//  * Set baud-rate.
	//  */
	// tty->c_cflag |= dev.rate;

    // /* Ignore framing and parity errors in input. */
    // tty->c_iflag |=  IGNPAR;

    // /* Use 8-bit characters. This too may affect standard streams,
    //  * but any sane C library can deal with 8-bit characters. */
    // tty->c_cflag |=  CS8;

    // /* Enable receiver. */
    // tty->c_cflag |=  CREAD;
    // tty->c_cc[VTIME] = 0;
    // tty->c_cc[VMIN] = 1;

	unsigned int speed = 2400;
 	cfsetospeed(tty, B2400);
    cfsetispeed(tty, B2400);

	tty->c_lflag &= ~ICANON;//关闭按行读取
	tty->c_lflag &= ~ISIG; //关闭特殊字符
    tty->c_cflag |= CLOCAL | CREAD;
    tty->c_cflag &= ~CSIZE;
    tty->c_cflag |= CS8;         /* 8-bit characters */
    tty->c_cflag |= PARENB;      /* enable parity */
    tty->c_cflag &= ~PARODD;     /* Even parity */
    tty->c_cflag |= CMSPAR;      /* force Even parity to SPACE */
    tty->c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty->c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */
	tty->c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
	tty->c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed

    //tty->c_lflag |= ICANON | ISIG;  /* canonical input */
    // tty->c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

    // tty->c_iflag &= ~IGNCR;  /* preserve carriage return */
    // tty->c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
    tty->c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
    // tty->c_iflag |= IGNBRK;  /* ignore breaks */
    // tty->c_iflag &= ~ISTRIP;
    // tty->c_iflag &= ~IGNPAR; /* report error */
    // tty->c_iflag |= INPCK;   /* test parity */
    // tty->c_iflag |= PARMRK;  /* verbose parity err */

	//对于串口读取很重要
	/*
	VMIN = 0，VTIME = 0:没有阻塞，立即返回有什么可用的

	VMIN > 0，VTIME = 0:这会使read()总是等待字节(确切的字节数由VMIN)，所以read()可能会无限期阻塞。

	VMIN = 0，VTIME > 0:这是具有最大超时的任意数量字符的阻塞读取(由下式给出VTIME). read()将一直阻塞，直到有足够的数据可用或者超时。这恰好是我最喜欢的模式(也是我用得最多的一个)。

	VMIN > 0，VTIME > 0:阻塞，直到VMIN字符已被接收，或VTIME第一个字符过去后。请注意VTIME直到收到第一个字符才开始。

	VMIN和VTIME都被定义为类型cc_t，我一直认为它是无符号字符(1字节)的别名。这就为VMIN字符数为255，最大超时为25.5秒(255分钟)
	*/
	tty->c_cc[VTIME] = 1;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
	//deciseconds代表是十分之一秒，1秒相当于1000毫秒，十分之一就是100毫秒
	//如果实在等了太久，也没有办法了，不能一直阻塞在那里
	tty->c_cc[VMIN] = DL645_RESP_LEN;
	//DL645_RESP_LEN 保证整段报文不管多长能够读进来

	/*
	 * Flush port.
	 */
	tcflush(fd, TCIFLUSH);

	/*
	 * Apply attributes.
	 */
	rc = tcsetattr(fd, TCSANOW, tty);
	if (rc) {
		printf("%s: failed to set attributes\r\n", __func__);
		return rc;
	}
    
	dev.fd= fd;
	dev.tty = tty;
	printf("tcsetattr\n");

	// g_dlt645.write = dlt645_hw_write;

	return 0;
}



//A static global variable or a function is "seen" only in the file it's declared in
//static dlt645_t dlt645静态全局变量或函数只在声明它的文件中“可见”
//导致在sample里的main函数调用的时候，这个没有被赋值
dlt645_t dlt645 = {
    {0},
    0,
    dlt645_hw_write,
    dlt645_hw_read,
    (void *)&dev};

void dlt645_close_connection(){
	//释放当前分配的内存空间
	close(((UartDevice *)(dlt645.port_data))->fd);
}